-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Fix/Make proc_macro span_close() and span_open() more accurate after set_span() calls #149052
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
rustbot has assigned @petrochenkov. Use |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I think it would be better to test the opening/closing span recovery strategy on the compiler first, and then do the same for proc macros.
|
I can look at adding something to the compiler side of things first. I suppose we could inspect parts of the stream itself if that is desired. However, at least on the proc_macro side I did not do that. That's because someone could assign a span not directly associated to the underlying stream that the group contains. Therefore, inspecting the stream may not provide any useful information. Thusly, I chose what I did since it would more accurate in most cases than just using the whole span. From a quick look the As for the four options:
As for the diagnostic output is there anything you're particularly looking for or wanting to avoid when you mention experimentally testing? |
I was not suggesting to inspect token stream, "source map" is some source text to which all spans map, not tokens. |
Just want to look at the results with different options for a start. |
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
This comment has been minimized.
This comment has been minimized.
So I have here the Honestly, in the compiler I would say that it might better to have not just some catch all This still does nothing about my main concern though about fixing the |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Could you update the tests (to make CI green) and submit the compiler changes in a separate PR? |
…r set_span call. This tries to recover the open in and close delimiter spans for the new span, otherwise it reverts to old behavior of using the the whole span for the open and close delimiter.
|
Okay, I updated the this to only handle the obvious case. I also make sure the the current delimiter of the group matches if not it just goes to the old behavior. I also thought about maybe adding this, but was not sure we wanted to loop through the source text like this? My reasoning was a span in theory could be for a fragment of text like this let mut depth = 1usize;
let mut iter = src.iter().skip(1);
while depth > 0
&& let Some(&c) = iter.next()
{
if c == open_chr {
depth += 1;
}
if c == close_chr {
depth -= 1;
}
}
// We possibly don't have a true matching pair because the depth did not
// reach zero or the iterator did not finish. This could be a false
// positive because of comments and literals. However, we are falling
// back to the default.
if depth != 0 || iter.next().is_some() {
self.0.span = bridge::DelimSpan::from_single(span.0);
return;
}The false positive I am talking about is something like this: { /* comment } */ let _code = 0; }So it would revert to old behavior, however in cases where there is no delimiter character in like a string literal or comment it still would allow @rustbot ready -- Edit -- |
|
Requested reviewer is already assigned to this pull request. Please choose another assignee. |
|
|
||
| // No source text to check or source text is too small. | ||
| // just go back to default behavior. | ||
| let source = match span.0.source_text() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like quite the expensive operation to call here. For one this goes through the bridge (which is expensive for rust-analyzer, especially this call, as once we implement it will always do an RPC roundtrip), but that aside, this also always allocates a new string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The allocation and the further bridge calls to subspan can be avoided by moving this whole logic into the compiler's side of the bridge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative solution is to just add fn set_span_(open,close) to the API.
Typically the user of set_span knows whether the passed span actually contains the group's source text, and if it's known, then set_span_(open,close) can be called as well.
I don't suspect it happens often, I'd expect set_span to be set to something semi-related from the macro input.
If we do this, then #149229 probably shouldn't be merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do this, then #149229 probably shouldn't be merged.
Or it can be merged if set_span_open changes only the opening span, set_span_close changes only the closing span, and set_span changes both to the same value.
This would also solve the question of what goes back to the compiler when proc_macro::Span is converted to compiler::Span.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do this, then #149229 probably shouldn't be merged.
Or it can be merged if
set_span_openchanges only the opening span,set_span_closechanges only the closing span, andset_spanchanges both to the same value.This would also solve the question of what goes back to the compiler when
proc_macro::Spanis converted tocompiler::Span.
A set_span_open() and set_span_close(), only effecting their respective fields and span set_span() overwriting both seems fine. Also if we want to add those API or similar API, #149229 makes even more sense because the entire field becomes more awkward with those proposed API.
Although for a public API we probably want that API to be set_span_open_close(Span, Span) to at least to make sure the spans are from the same file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The allocation and the further bridge calls to
subspancan be avoided by moving this whole logic into the compiler's side of the bridge.
That is a good point and certainly do able.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to at least to make sure the spans are from the same file
That also requires going through the bridge though.
All of this is a heuristic, using the full span is also not a precise solution, so it's fine, I think. |
I also realized after going to bed last night such a case is probably very unlikely under typical usage. This is because the compiler would have broken those up into separate group tokens. |
When
Group::set_span()is called the span's forGroup::span_open()andGroup::span_close()methods just become the whole span instead where it should be the span of the delimiters. This PR ensures they have at least a more usable value after a call toGroup::set_span().Here is a kinda of motivating example of this issue I observed. If you compare the current behavior the outputs the diagnostics will not be on the opening and closing delimiters after calling set span. If you look at the outputs after using this PR they will match. Further, afterward
Span::eq()then evaluates to true which nice to see.This is all I did to invoke the proc_macro example above.